/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice.persistence.derivative;

import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.core.base.Namespace;
import org.proteusframework.platformservice.persistence.c14n.C14NFactory;
import org.proteusframework.platformservice.persistence.messagebean.DataType;

import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.*;
import java.util.Date;

/**
 * @author Tacoma Four
 */
final class JDBCDerivativeConverter implements IDerivativeConverter<PreparedStatement>
{
    @Override
    public void convert(PreparedStatement targetDerivative, DataType dataType, Object value, Object... parameters)
    {
        if (parameters.length < 1)
            throw new IllegalArgumentException("Parameters must include an Integer position");

        if (!parameters[0].getClass().equals(Integer.class))
            throw new IllegalArgumentException("Parameter position 0 must be an Integer object");

        Integer position = (Integer) parameters[0];
        try
        {
            switch (dataType)
            {
                case BigDecimalType:
                    if (null != value)
                    {
                        targetDerivative.setBigDecimal(position, (BigDecimal) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.NUMERIC);
                    }
                    break;
                case BigIntegerType:
                    if (null != value)
                    {
                        targetDerivative.setLong(position, (Long) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BIGINT);
                    }
                    break;
                case BooleanArrayType:
                    if (null != value)
                    {
                        byte[] booleanBytes = C14NFactory.getHandler(DataType.BooleanArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(booleanBytes), booleanBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case BooleanType:
                    if (null != value)
                    {
                        targetDerivative.setBoolean(position, (Boolean) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BOOLEAN);
                    }
                    break;
                case ByteArrayType:
                    if (null != value)
                    {
                        byte[] bytes = (byte[]) value;
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(bytes), bytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case ByteType:
                    if (null != value)
                    {
                        targetDerivative.setByte(position, (Byte) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.INTEGER);
                    }
                    break;
                case CharArrayType:
                    if (null != value)
                    {
                        char[] chars = (char[]) value;
                        targetDerivative.setCharacterStream(position, new StringReader(String.valueOf(chars)), chars.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.CLOB);
                    }
                    break;
                case CharType:
                    if (null != value)
                    {
                        targetDerivative.setString(position, value.toString());
                    } else
                    {
                        targetDerivative.setNull(position, Types.VARCHAR);
                    }
                    break;
                case DateArrayType:
                    if (null != value)
                    {
                        byte[] dateBytes = C14NFactory.getHandler(DataType.DateArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(dateBytes), dateBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case DateType:
                    if (null != value)
                    {
                        targetDerivative.setDate(position, new java.sql.Date(((Date) value).getTime()));
                    } else
                    {
                        targetDerivative.setNull(position, Types.DATE);
                    }
                    break;
                case DoubleArrayType:
                    if (null != value)
                    {
                        byte[] doubleBytes = C14NFactory.getHandler(DataType.DoubleArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(doubleBytes), doubleBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case DoubleType:
                    if (null != value)
                    {
                        targetDerivative.setDouble(position, (Double) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.DOUBLE);
                    }
                    break;
                case EnumType:
                    if (null != value)
                    {
                        targetDerivative.setString(position, value.toString());
                    } else
                    {
                        targetDerivative.setNull(position, Types.VARCHAR);
                    }
                    break;
                case FloatArrayType:
                    if (null != value)
                    {
                        byte[] floatBytes = C14NFactory.getHandler(DataType.FloatArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(floatBytes), floatBytes.length);

                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case FloatType:
                    if (null != value)
                    {
                        targetDerivative.setFloat(position, (Float) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.FLOAT);
                    }
                    break;
                case IntegerArrayType:
                    if (null != value)
                    {
                        byte[] intBytes = C14NFactory.getHandler(DataType.IntegerArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(intBytes), intBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case IntegerType:
                    if (null != value)
                    {
                        targetDerivative.setInt(position, (Integer) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.INTEGER);
                    }
                    break;
                case LongArrayType:
                    if (null != value)
                    {
                        byte[] longBytes = C14NFactory.getHandler(DataType.LongArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(longBytes), longBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case LongType:
                    if (null != value)
                    {
                        targetDerivative.setLong(position, (Long) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.INTEGER);
                    }
                    break;
                case NamespaceArrayType:
                    if (null != value)
                    {
                        byte[] nsBytes = C14NFactory.getHandler(DataType.NamespaceArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(nsBytes), nsBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case NamespaceType:
                    if (null != value)
                    {
                        targetDerivative.setString(position, Namespace.toCanonical((INamespace) value, true));
                    } else
                    {
                        targetDerivative.setNull(position, Types.VARCHAR);
                    }
                    break;
                case SerializableType:
                    if (null != value)
                    {
                        byte[] dosBytes = C14NFactory.getHandler(DataType.SerializableType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(dosBytes), dosBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case ShortArrayType:
                    if (null != value)
                    {
                        byte[] shortBytes = C14NFactory.getHandler(DataType.ShortArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(shortBytes), shortBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case ShortType:
                    if (null != value)
                    {
                        targetDerivative.setShort(position, (Short) value);
                    } else
                    {
                        targetDerivative.setNull(position, Types.SMALLINT);
                    }
                    break;
                case StringArrayType:
                    if (null != value)
                    {
                        byte[] stringBytes = C14NFactory.getHandler(DataType.StringArrayType).toC14N(value);
                        targetDerivative.setBinaryStream(position, new ByteArrayInputStream(stringBytes), stringBytes.length);
                    } else
                    {
                        targetDerivative.setNull(position, Types.BLOB);
                    }
                    break;
                case StringType:
                    if (null != value)
                    {
                        targetDerivative.setString(position, value.toString());
                    } else
                    {
                        targetDerivative.setNull(position, Types.VARCHAR);
                    }
                    break;
                case TimeType:
                    if (null != value)
                    {
                        if (value instanceof java.util.Date)
                        {
                            targetDerivative.setTime(position, new Time(((Date) value).getTime()));
                        } else
                        {
                            targetDerivative.setTime(position, (Time) value);
                        }
                    } else
                    {
                        targetDerivative.setNull(position, Types.TIME);
                    }
                    break;
                case TimestampType:
                    if (null != value)
                    {
                        if (value instanceof java.util.Date)
                        {
                            targetDerivative.setTimestamp(position, new Timestamp(((Date) value).getTime()));
                        } else
                        {
                            targetDerivative.setTimestamp(position, (Timestamp) value);
                        }
                    } else
                    {
                        targetDerivative.setNull(position, Types.TIMESTAMP);
                    }
                    break;
                case URLType:
                    if (null != value)
                    {
                        targetDerivative.setString(position, value.toString());
                    } else
                    {
                        targetDerivative.setNull(position, Types.VARCHAR);
                    }
                    break;
                case UUIDType:
                    if (null != value)
                    {
                        targetDerivative.setString(position, value.toString());
                    } else
                    {
                        targetDerivative.setNull(position, Types.VARCHAR);
                    }
                    break;
                case XmlType:
                    if (null != value)
                    {
                        targetDerivative.setAsciiStream(position, new ByteArrayInputStream(value.toString().getBytes()));
                    } else
                    {
                        targetDerivative.setNull(position, Types.CLOB);

                    }
                    break;
            }

        } catch (SQLException e)
        {
            e.printStackTrace();
            throw new IllegalStateException("Unable to complete derivative conversion: " + e.getMessage());
        }
    }
}
